﻿using System;
using System.Collections.Generic;
using System.Linq;
using Assist;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Plumbing;
using Autodesk.Revit.UI;
using YArchitech.Revit;
using YArchitech.Revit.RevitExtension;
using Transfer.Utils;

namespace YArchitech.Plumbing
{
	public class DrawMultiPipeUtility
	{
		public static Line GetParallelLineByDistance(XYZ start, XYZ end, double distance)
		{
			Line result = null;
			try
			{
				XYZ xyz = (end - start).Normalize();
				XYZ xyz2 = Transform.CreateRotation(new XYZ(0.0, 0.0, -1.0), Math.PI*.5).OfVector(xyz);
				XYZ startPoint = start.Add(xyz2.Normalize() * AssistFunc.mmToFeet(distance));
				XYZ endPoint = end.Add(xyz2.Normalize() * AssistFunc.mmToFeet(distance));
				result = YJKLineEx.YJKGetBound(startPoint, endPoint);
			}
			catch (Exception ex)
			{
				ex.ToString();
				throw ex;
			}
			return result;
		}

		public static List<DrawPipeStruct> GetPipeInfo(Document doc, List<Pipe> pipeList, double? newDistance)
		{
			List<DrawPipeStruct> list = new List<DrawPipeStruct>();
			for (int i = 0; i < pipeList.Count; i++)
			{
				DrawPipeStruct drawPipeStruct = new DrawPipeStruct();
				drawPipeStruct.pipeType = pipeList[i].PipeType;
				drawPipeStruct.pipeDiameter = AssistFunc.feetToMM(pipeList[i].Diameter);
				drawPipeStruct.levelOffSet = AssistFunc.feetToMM(pipeList[i].GetParameter(BuiltInParameter.RBS_OFFSET_PARAM).AsDouble());
				Parameter parameter = pipeList[i].GetParameter(BuiltInParameter.RBS_PIPING_SYSTEM_TYPE_PARAM);
				if (parameter != null)
				{
					ElementId elementId = parameter.AsElementId();
					drawPipeStruct.pipingSystem = (doc.GetElementById(elementId) as PipingSystemType);
				}
				if (i == 0)
				{
					drawPipeStruct.offSet = 0.0;
				}
				else if (i > 0 && i < pipeList.Count)
				{
					if (newDistance == null)
					{
						Line otherLine = (pipeList[i - 1].Location as LocationCurve).Curve as Line;
						YJKLine YJKLine = new YJKLine((pipeList[i].Location as LocationCurve).Curve as Line);
						drawPipeStruct.offSet = AssistFunc.feetToMM(YJKLine.GetDistance(otherLine));
					}
					else
					{
						drawPipeStruct.offSet = newDistance.Value;
					}
				}
				list.Add(drawPipeStruct);
			}
			return list;
		}

		public static List<List<LineInfo>> DrawMultiLines(List<XYZ> OripointList, XYZ fromPoint, XYZ endPoint, List<double> intervals, bool isAdd)
		{
			List<XYZ> list = new List<XYZ>();
			if (OripointList.Count < 4 && isAdd && fromPoint != null)
			{
				list.Add(fromPoint);
			}
			foreach (XYZ xyz in OripointList)
			{
				XYZ item = new XYZ(xyz.X, xyz.Y, xyz.Z);
				list.Add(item);
			}
			List<List<LineInfo>> list2 = new List<List<LineInfo>>();
			try
			{
				if (list != null && list.Count > 1 && intervals.Count > 0)
				{
					List<LineInfo> list3 = new List<LineInfo>();
					List<LineInfo> list4 = new List<LineInfo>();
					XYZ xyz2 = list[0];
					XYZ xyz3 = list[1];
					if (!Geometry.IsEqual(intervals[0], 0.0))
					{
						Line parallelLineByDistance = DrawMultiPipeUtility.GetParallelLineByDistance(xyz2, xyz3, intervals[0]);
						if (parallelLineByDistance != null)
						{
							XYZ xyz4 = parallelLineByDistance.GetEndPoint(0);
							XYZ hyendPoint = parallelLineByDistance.GetEndPoint(1);
							xyz2 = xyz4;
							xyz3 = hyendPoint;
						}
					}
					list3.Add(new LineInfo(xyz2, xyz3));
					double num = 0.0;
					for (int i = 1; i < intervals.Count; i++)
					{
						num += intervals[i];
						Line parallelLineByDistance = DrawMultiPipeUtility.GetParallelLineByDistance(xyz2, xyz3, num);
						if (parallelLineByDistance != null)
						{
							XYZ xyz4 = parallelLineByDistance.GetEndPoint(0);
							XYZ hyendPoint = parallelLineByDistance.GetEndPoint(1);
							list3.Add(new LineInfo(xyz4, hyendPoint));
						}
					}
					list2.Add(list3);
					if (list.Count > 2)
					{
						for (int j = 2; j < list.Count + 1; j++)
						{
							list4 = new List<LineInfo>();
							xyz2 = xyz3;
							if (list.Count == j)
							{
								if (endPoint == null)
								{
									break;
								}
								xyz3 = endPoint;
							}
							else
							{
								xyz3 = list[j];
							}
							if (!Geometry.IsEqual(intervals[0], 0.0))
							{
								if (list.Count == j)
								{
									if (endPoint == null)
									{
										break;
									}
									Line parallelLineByDistance = DrawMultiPipeUtility.GetParallelLineByDistance(list[j - 1], endPoint, intervals[0]);
									XYZ xyz4 = parallelLineByDistance.GetEndPoint(0);
									XYZ hyendPoint = parallelLineByDistance.GetEndPoint(1);
									YJKLine YJKLine = new YJKLine(parallelLineByDistance);
									Line otherLine = YJKLineEx.YJKGetBound(list3[0].StartPoint, list3[0].EndPoint);
									if (!YJKLine.IsParallel(otherLine))
									{
										XYZ xyz5 = YJKLine.IntersectionPoint(otherLine, false);
										if (xyz5 != null)
										{
											xyz4 = xyz5;
											list3[0].EndPoint = xyz5;
										}
									}
									xyz2 = xyz4;
									xyz3 = hyendPoint;
								}
								else
								{
									Line parallelLineByDistance = DrawMultiPipeUtility.GetParallelLineByDistance(list[j - 1], list[j], intervals[0]);
									XYZ xyz4 = parallelLineByDistance.GetEndPoint(0);
									XYZ hyendPoint = parallelLineByDistance.GetEndPoint(1);
									YJKLine YJKLine2 = new YJKLine(parallelLineByDistance);
									Line otherLine2 = YJKLineEx.YJKGetBound(list3[0].StartPoint, list3[0].EndPoint);
									if (!YJKLine2.IsParallel(otherLine2))
									{
										XYZ xyz6 = YJKLine2.IntersectionPoint(otherLine2, false);
										if (xyz6 != null)
										{
											xyz4 = xyz6;
											list3[0].EndPoint = xyz6;
										}
									}
									xyz2 = xyz4;
									xyz3 = hyendPoint;
								}
							}
							list4.Add(new LineInfo(xyz2, xyz3));
							num = 0.0;
							for (int k = 1; k < intervals.Count; k++)
							{
								num += intervals[k];
								Line parallelLineByDistance = DrawMultiPipeUtility.GetParallelLineByDistance(xyz2, xyz3, num);
								XYZ xyz4 = parallelLineByDistance.GetEndPoint(0);
								XYZ hyendPoint = parallelLineByDistance.GetEndPoint(1);
								YJKLine YJKLine3 = new YJKLine(parallelLineByDistance);
								Line otherLine3 = YJKLineEx.YJKGetBound(list3[k].StartPoint, list3[k].EndPoint);
								if (!YJKLine3.IsParallel(otherLine3))
								{
									XYZ xyz7 = YJKLine3.IntersectionPoint(otherLine3, false);
									if (xyz7 != null)
									{
										xyz4 = xyz7;
										list3[k].EndPoint = xyz7;
									}
								}
								list4.Add(new LineInfo(xyz4, hyendPoint));
							}
							if (list2.Count > 0)
							{
								list2.RemoveAt(list2.Count - 1);
							}
							list2.Add(list3);
							list2.Add(list4);
							list3 = new List<LineInfo>();
							foreach (LineInfo lineInfo in list4)
							{
								list3.Add(new LineInfo(lineInfo.StartPoint, lineInfo.EndPoint));
							}
						}
					}
				}
			}
			catch (Exception ex)
			{
				ex.ToString();
			}
			return list2;
		}

		public static XYZ GetAxisPoint(UIDocument uiDoc, XYZ ptUser, XYZ ptOrigin)
		{
			if (ptUser.DistanceTo(ptOrigin) < 0.0032808398950131233)
			{
				return null;
			}
			Autodesk.Revit.DB.View activeView = uiDoc.ActiveView;
			XYZ xyzpoint = Line.CreateUnbound(ptOrigin, activeView.RightDirection).Project(ptUser).XYZPoint;
			if (xyzpoint.DistanceTo(ptOrigin) > 0.1 && DrawMultiPipeUtility.Get3PtAngle(xyzpoint, ptOrigin, ptUser) < 0.052359877559829883)
			{
				return xyzpoint;
			}
			xyzpoint = Line.CreateUnbound(ptOrigin, activeView.UpDirection).Project(ptUser).XYZPoint;
			if (xyzpoint.DistanceTo(ptOrigin) > 0.1 && DrawMultiPipeUtility.Get3PtAngle(xyzpoint, ptOrigin, ptUser) < 0.052359877559829883)
			{
				return xyzpoint;
			}
			Transform transform = Transform.CreateRotation(activeView.ViewDirection, 0.78539816339744828);
			XYZ xyz = transform.OfVector(activeView.RightDirection);
			xyzpoint = Line.CreateUnbound(ptOrigin, xyz).Project(ptUser).XYZPoint;
			if (xyzpoint.DistanceTo(ptOrigin) > 0.1 && DrawMultiPipeUtility.Get3PtAngle(xyzpoint, ptOrigin, ptUser) < 0.052359877559829883)
			{
				return xyzpoint;
			}
			xyz = transform.OfVector(activeView.UpDirection);
			xyzpoint = Line.CreateUnbound(ptOrigin, xyz).Project(ptUser).XYZPoint;
			if (xyzpoint.DistanceTo(ptOrigin) > 0.1 && DrawMultiPipeUtility.Get3PtAngle(xyzpoint, ptOrigin, ptUser) < 0.052359877559829883)
			{
				return xyzpoint;
			}
			return null;
		}

		public static double Get3PtAngle(XYZ pt1, XYZ ptOrigin, XYZ pt2)
		{
			XYZ xyz = pt1 - ptOrigin;
			XYZ xyz2 = pt2 - ptOrigin;
			return xyz.AngleTo(xyz2);
		}

		public static List<List<LineInfo>> DrawMultiLines(List<XYZ> OripointList, List<double> intervals, XYZ offsetDirection)
		{
			List<XYZ> list = new List<XYZ>();
			foreach (XYZ xyz in OripointList)
			{
				XYZ item = new XYZ(xyz.X, xyz.Y, xyz.Z);
				list.Add(item);
			}
			List<List<LineInfo>> list2 = new List<List<LineInfo>>();
			try
			{
				if (list != null && list.Count > 1 && intervals.Count > 0)
				{
					List<LineInfo> list3 = new List<LineInfo>();
					XYZ xyz2 = list[0];
					XYZ end = list[1];
					if (!Geometry.IsEqual(intervals[0], 0.0))
					{
						Line parallelLineByDistance = DrawMultiPipeUtility.GetParallelLineByDistance(xyz2, end, intervals[0], offsetDirection);
						if (parallelLineByDistance != null)
						{
							XYZ hyendPoint = parallelLineByDistance.GetEndPoint(0);
							XYZ hyendPoint2 = parallelLineByDistance.GetEndPoint(1);
							xyz2 = hyendPoint;
							end = hyendPoint2;
						}
					}
					list3.Add(new LineInfo(xyz2, end));
					double num = 0.0;
					for (int i = 1; i < intervals.Count; i++)
					{
						num += intervals[i];
						Line parallelLineByDistance = DrawMultiPipeUtility.GetParallelLineByDistance(xyz2, end, num, offsetDirection);
						if (parallelLineByDistance != null)
						{
							XYZ hyendPoint = parallelLineByDistance.GetEndPoint(0);
							XYZ hyendPoint2 = parallelLineByDistance.GetEndPoint(1);
							list3.Add(new LineInfo(hyendPoint, hyendPoint2));
						}
					}
					list2.Add(list3);
				}
			}
			catch (Exception ex)
			{
				ex.ToString();
			}
			return list2;
		}

		public static Line GetParallelLineByDistance(XYZ start, XYZ end, double distance, XYZ offsetDirection)
		{
			Line result = null;
			try
			{
				XYZ startPoint = start.Add(offsetDirection * AssistFunc.mmToFeet(distance));
				XYZ endPoint = end.Add(offsetDirection * AssistFunc.mmToFeet(distance));
				result = YJKLineEx.YJKGetBound(startPoint, endPoint);
			}
			catch (Exception ex)
			{
				ex.ToString();
				throw ex;
			}
			return result;
		}

		public static List<List<LineInfo>> DrawMutilFromExistingPipes(List<ExistingPipeStruct> existingPipeStructs, XYZ startPoint, XYZ currentPoint, DeriPipeOffSet deriPipeOffSet, double? distance)
		{
			List<List<LineInfo>> list = new List<List<LineInfo>>();
			List<LineInfo> list2 = new List<LineInfo>();
			double num = 0.0;
			if (distance != null)
			{
				num = Convert.ToDouble(distance);
			}
			XYZ pipeDirection = existingPipeStructs.FirstOrDefault((ExistingPipeStruct p) => p.pipeDirection != null).pipeDirection;
			XYZ xyz = new XYZ(currentPoint.X, currentPoint.Y, startPoint.Z);
			XYZ xyz2 = (xyz - startPoint).Normalize();
			XYZ xyz3 = Transform.CreateRotation(new XYZ(0.0, 0.0, -1.0), Math.PI*.5).OfVector(xyz2).Normalize();
			foreach (ExistingPipeStruct existingPipeStruct in existingPipeStructs)
			{
				LineInfo lineInfo;
				if (existingPipeStruct.serialNumber == 0)
				{
					lineInfo = new LineInfo(startPoint, xyz);
				}
				else if (DrawMultiPipeUtility.ReverseSort(pipeDirection, existingPipeStruct.verticalDirection, xyz2, xyz3, 0.01))
				{
					if (deriPipeOffSet == DeriPipeOffSet.OriginalPipeOff)
					{
						lineInfo = new LineInfo(startPoint.Subtract(existingPipeStruct.distance * xyz3), xyz.Subtract(existingPipeStruct.distance * xyz3));
					}
					else
					{
						lineInfo = new LineInfo(startPoint.Subtract((double)existingPipeStruct.serialNumber * xyz3 * num / 304.8), xyz.Subtract((double)existingPipeStruct.serialNumber * xyz3 * num / 304.8));
					}
				}
				else if (deriPipeOffSet == DeriPipeOffSet.OriginalPipeOff)
				{
					lineInfo = new LineInfo(startPoint.Add(existingPipeStruct.distance * xyz3), xyz.Add(existingPipeStruct.distance * xyz3));
				}
				else
				{
					lineInfo = new LineInfo(startPoint.Add((double)existingPipeStruct.serialNumber * xyz3 * num / 304.8), xyz.Add((double)existingPipeStruct.serialNumber * xyz3 * num / 304.8));
				}
				lineInfo.pipe = existingPipeStruct.pipe;
				list2.Add(lineInfo);
				list.Add(list2);
			}
			return list;
		}

		public static bool ReverseSort(XYZ lastDir, XYZ lastVer, XYZ newDir, XYZ newVer, double tolerance = 0.005)
		{
			bool result = false;
			if (lastDir.AngleTo(newDir) < Math.PI*.5 - tolerance)
			{
				if (lastDir.AngleTo(newDir) <= tolerance)
				{
					if (newVer.AngleTo(-lastVer) <= tolerance)
					{
						result = true;
					}
				}
				else if (lastVer.AngleTo(newVer) > Math.PI*.5 + tolerance)
				{
					result = true;
				}
			}
			else if (lastDir.AngleTo(newDir) >= Math.PI*.5 - tolerance && lastDir.AngleTo(newDir) <= Math.PI*.5 + tolerance)
			{
				if (newVer.AngleTo(-lastDir) <= tolerance)
				{
					if (lastVer.AngleTo(-newDir) <= tolerance)
					{
						result = true;
					}
				}
				else if (newVer.AngleTo(lastDir) <= tolerance && lastVer.AngleTo(newDir) <= tolerance)
				{
					result = true;
				}
			}
			else if (lastDir.AngleTo(-newDir) <= tolerance)
			{
				if (newVer.AngleTo(-lastVer) <= tolerance)
				{
					result = true;
				}
			}
			else if (lastVer.AngleTo(newVer) < Math.PI*.5 - tolerance)
			{
				result = true;
			}
			return result;
		}

		public static List<DrawPipeStruct> GetPipeInfo(Document doc, List<ExistingPipeStruct> pipeLines, double? newDistance, double pipeLevel)
		{
			List<DrawPipeStruct> list = new List<DrawPipeStruct>();
			for (int i = 0; i < pipeLines.Count; i++)
			{
				DrawPipeStruct drawPipeStruct = new DrawPipeStruct();
				drawPipeStruct.Pipe = pipeLines[i].pipe;
				drawPipeStruct.pipeType = pipeLines[i].pipe.PipeType;
				drawPipeStruct.pipeDiameter = AssistFunc.feetToMM(pipeLines[i].pipe.Diameter);
				drawPipeStruct.levelOffSet = AssistFunc.feetToMM(pipeLines[i].pipe.GetParameter(BuiltInParameter.RBS_OFFSET_PARAM).AsDouble()) + pipeLevel;
				Parameter parameter = pipeLines[i].pipe.GetParameter(BuiltInParameter.RBS_PIPING_SYSTEM_TYPE_PARAM);
				if (parameter != null)
				{
					ElementId elementId = parameter.AsElementId();
					drawPipeStruct.pipingSystem = (doc.GetElementById(elementId) as PipingSystemType);
				}
				if (i == 0)
				{
					drawPipeStruct.offSet = 0.0;
				}
				else if (i > 0 && i < pipeLines.Count)
				{
					if (newDistance == null)
					{
						drawPipeStruct.offSet = AssistFunc.feetToMM(pipeLines[i].distance);
					}
					else
					{
						drawPipeStruct.offSet = newDistance.Value;
					}
				}
				list.Add(drawPipeStruct);
			}
			return list;
		}
	}
}
